package com.yx.changdao.web.handler;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.yx.changdao.common.annotation.AutoFill;
import com.yx.changdao.common.data.ResultCodeEnum;
import com.yx.changdao.common.exception.AlertException;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.reflection.MetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import java.util.HashMap;

/**
 * @Title: CommonMetaObjectHandler
 * @Description: 数据自动填充
 * @Auther: Zhangjiashun
 * @Version: 1.0
 * @create 2019/11/5 11:30
 */
@Component
public class CommonMetaObjectHandler implements MetaObjectHandler {

    private static final Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @Override
    public void insertFill(MetaObject metaObject) {
        try {
            this.autoInsertFill(metaObject);
        } catch (AlertException e) {
            logger.info("================ 新增时填充失败 ================" + e.getMessage());
        }
    }


    @Override
    public void updateFill(MetaObject metaObject) {
        try {
            this.autoUpdateFill(metaObject);
        } catch (AlertException e) {
            logger.info("================ 更新时填充失败 ================" + e.getMessage());
        }
    }


    /**
     * 插入自动填充
     *
     * @param metaObject 元对象
     * @throws AlertException 异常
     */
    private void autoInsertFill(MetaObject metaObject) throws AlertException {
        // 插入时元对象 getOriginalObject 就是操作的实体
        Class<?> clazz = metaObject.getOriginalObject().getClass();
        Field[] declaredFields = clazz.getDeclaredFields();
        // 获取所有需要插入填充的属性
        for (Field field : declaredFields) {
            TableField tableField = field.getAnnotation(TableField.class);
            AutoFill autoFill = field.getAnnotation(AutoFill.class);
            // 该属性存在以上两个注解
            if (tableField != null && autoFill != null) {
                // 字段填充类型是插入。
                if (tableField.fill() == FieldFill.INSERT || tableField.fill() == FieldFill.INSERT_UPDATE) {
                    this.setAutoFillValue(metaObject, field);
                }
            }
        }
    }

    /**
     * 更新自动填充
     *
     * @param metaObject 元对象
     * @throws AlertException 异常
     */
    private void autoUpdateFill(MetaObject metaObject) throws AlertException {
        // 在Service中使用baseMapper对对象进行更新时
        // metaObject元对象的getOriginalObject 是一个Map
        // 需要取得Map的 et或param 所对应的字段才是操作的实体

        Class<?> clazz;
        try {
            clazz = ((HashMap) metaObject.getOriginalObject()).get("et").getClass();
        } catch (ClassCastException e) {
            clazz = metaObject.getOriginalObject().getClass();
        }

        Field[] declaredFields = clazz.getDeclaredFields();
        // 获取所有需要更新插入填充的属性
        for (Field field : declaredFields) {
            TableField tableField = field.getAnnotation(TableField.class);
            AutoFill autoFill = field.getAnnotation(AutoFill.class);
            // 该属性存在以上两个注解
            if (tableField != null && autoFill != null) {
                // 字段填充类型是更新。
                if (tableField.fill() == FieldFill.UPDATE || tableField.fill() == FieldFill.INSERT_UPDATE) {
                    this.setAutoFillValue(metaObject, field);
                }
            }
        }
    }

    /**
     * 设置自动填充值。
     *
     * @param metaObject 元对象
     * @param field      需要填充的字段
     * @throws AlertException 异常
     */
    private void setAutoFillValue(MetaObject metaObject, Field field) throws AlertException {
        AutoFill autoFill = field.getAnnotation(AutoFill.class);
        if (StringUtils.isNotBlank(autoFill.method())) {
            // 获取方法名
            String methodName = autoFill.method();
            // autoFill 中的值不为空 调用所指定类的指定方法
            try {
                // 类名
                Class<?> claz = autoFill.klass();
                // 方法名
                Method method = claz.getMethod(methodName);
                // 调用方法。
                Object invoke = method.invoke(claz.newInstance());
                // 将调用后的返回结果填充到该属性上。
                this.setFieldValByName(field.getName(), invoke, metaObject);
            } catch (NoSuchMethodException e) {
                throw new AlertException("找不到方法：" + methodName);
            } catch (InvocationTargetException ignored) {
            } catch (Exception e) {
                throw new AlertException(ResultCodeEnum.UNKNOWN_REASON.getMessage());
            }
        } else if (field.getType() == Date.class) {
            this.setFieldValByName(field.getName(), new Date(), metaObject);
        } else if (field.getType() == Timestamp.class) {
            this.setFieldValByName(field.getName(), new Timestamp(System.currentTimeMillis()), metaObject);
        } else {
            // 如果没有指定方法 且不是 Date 或者 Timestamp 类型默认赋值null;
            this.setFieldValByName(field.getName(), null, metaObject);
        }
    }


}
